/* 
 * File:   coreSound.cpp
 * Author: Serge
 * 
 * Created on 4 avril 2011, 20:53
 */

#include "coreSound.h"

//-----------------------------Constructor--------------------------------------
CoreSound::CoreSound(void)
{
    //Attibuts initialization---------------------------------------------------
    nbr_in_channels = 2;
    nbr_out_channels = 2;
    sample_rate = 48000;
    frame_per_buffer=32;
    is_initalized=false;
    is_running=false;
    

    //Portaudio initialization--------------------------------------------------
    err = Pa_Initialize();

    if(err == paNoError){
        //Flag as initialized
         is_initalized = true;

        /* default input device */
        deviceIndex = Pa_GetDefaultOutputDevice();
        inputParameters.device = deviceIndex;
        inputParameters.channelCount = nbr_in_channels;           //Stereo input
        inputParameters.sampleFormat = PA_SAMPLE_TYPE;
        inputParameters.suggestedLatency =
            Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
        inputParameters.hostApiSpecificStreamInfo = NULL;
        outputParameters.device = deviceIndex;                 //Default out dev
        outputParameters.channelCount = nbr_out_channels;        //Stereo output
        outputParameters.sampleFormat = PA_SAMPLE_TYPE;
        outputParameters.suggestedLatency =
            Pa_GetDeviceInfo(outputParameters.device )->defaultLowOutputLatency;
        outputParameters.hostApiSpecificStreamInfo = NULL;
    }
}

//-----------------------------Destructor---------------------------------------
CoreSound::~CoreSound(void)
{
    Pa_Terminate();
}

//-----------------------------Start streaming----------------------------------
int CoreSound::Start(){

    //Open stream
    err = Pa_OpenStream(&stream,
                        &inputParameters,
                        &outputParameters,
                        sample_rate,
                        frame_per_buffer,
                        0,                      //paClipOff, manage the clipping
                        callback,
                        NULL );

    if(err == paNoError){                                        //Stream opened
        err = Pa_StartStream( stream );                        //Start streaming
        if(err == paNoError){                                //Streaming started
            is_running = true;
            return paNoError;
        }else{                                          //Streaming failed start
            is_running = false;
            Pa_CloseStream(stream);
            return err;
        }
    }else{
        is_running = false;
        return err;
    }

}


//---------------------------STOP STREAMING-------------------------------------
int CoreSound::Stop(){
    if(is_running){                                         //if stream opened
        err = Pa_StopStream( stream );
        if(err == paNoError){
            err = Pa_CloseStream( stream );
            if(err == paNoError){
                return err;
            }else{
                return paNoError;
            }
        }else{
            return err;
        }
    }else{
        return paNoError;
    }
}


//------------------------------List devices-----------------------------------
string* CoreSound::ListDevicesNames()
{
	//Local variables
	int nbrDevices, i;
	string * devicesList;
	const PaDeviceInfo* tmpDevInfo;

	nbrDevices = Pa_GetDeviceCount();
	devicesList = new string[nbrDevices];

	for(i =0; i < nbrDevices; i++){
		tmpDevInfo = Pa_GetDeviceInfo(i);
		devicesList[i] = tmpDevInfo->name;
	}

	return devicesList;
}

//------------------------------List devices------------------------------------
int CoreSound::DevicesCount()
{
	return Pa_GetDeviceCount();
}

//------------------------------Change Device-----------------------------------
void CoreSound::ChangeDevice(int prmIndex)
{
    bool was_running = false;
    if(is_running){
        Stop();
        was_running = true;
    }
    inputParameters.device = prmIndex;
    outputParameters.device = prmIndex;
    deviceIndex = prmIndex;

    if(was_running){
        Start();
    }
    
}

//------------------------------Get Device Informations-------------------------
const PaDeviceInfo * CoreSound::GetDeviceInfo(int prmIndex)
{
    int tmpDeviceIndex;
    if(prmIndex != -1){
        tmpDeviceIndex = prmIndex;
    }else{
        tmpDeviceIndex = deviceIndex;
    }

    return Pa_GetDeviceInfo(tmpDeviceIndex);
}

//------------------------------Set sample Rate---------------------------------
void CoreSound::SetSampleRate(int prmSampleRate)
{
    bool was_running = false;
    if(is_running){
        Stop();
        was_running = true;
    }

    sample_rate = prmSampleRate;
    if(was_running){
        Start();
    }
}

//------------------------------Set resolution----------------------------------
void CoreSound::SetResolution(int prmFrame_per_buffer)
{
    bool was_running = false;
    if(is_running){
        Stop();
        was_running = true;
    }

    frame_per_buffer = prmFrame_per_buffer;
    if(was_running){
        Start();
    }
}
//---------------------------CALLBACK FUNCTION----------------------------------
int callback( const void *inputBuffer, void *outputBuffer,
                         unsigned long framesPerBuffer,
                         const PaStreamCallbackTimeInfo* timeInfo,
                         PaStreamCallbackFlags statusFlags,
                         void *userData )
{
    sample *out = (sample*)outputBuffer;
    const sample *in = (const sample*)inputBuffer;
    unsigned int i;
    (void) timeInfo; /* Prevent unused variable warnings. */
    (void) statusFlags;
    (void) userData;

    if( inputBuffer == NULL )
    {
        for( i=0; i<framesPerBuffer; i++ )
        {
            *out++ = 0;  /* left - silent */
            *out++ = 0;  /* right - silent */
        }
    }
    else
    {
        for( i=0; i<framesPerBuffer; i++ )
        {
            *out++ = *in++;  /* left - distorted */
            *out++ = *in++;          /* right - clean */
        }
    }

    return paContinue;
}